#include <stdio.h>
#include <stdlib.h>
#include <GL/glew.h>
#include "shaders.h"
#include <errno.h>
#include <string.h>

unsigned int create_shader(int type, const char *src)
{
	//Create shader and compile it
	unsigned int sdr = glCreateShader(type);
	glShaderSource(sdr, 1, &src, 0);
	glCompileShader(sdr);

	//Check compilation status
	int status;
	glGetShaderiv(sdr, GL_COMPILE_STATUS, &status);

	//Compilation failed
	if (!status)
	{
		//Get compilation log
		int logsz;
		glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &logsz);
		if (logsz > 1) {
			char *buf = new char[logsz +1];
			glGetShaderInfoLog(sdr, logsz, 0, buf);
			buf[logsz] = 0;
			printf("compilation of %s shader failed.\nshader compiler log:\n%s\n", 
				type == GL_VERTEX_SHADER   ? "vertex" :
				type == GL_FRAGMENT_SHADER ? "fragment" : 
				"<unknown>", buf);
			delete [] buf;
		}
		
		//Delete shader and return nothing
		glDeleteShader(sdr);
		return 0;
	}

	//Return shader
	return sdr;
}

unsigned int create_program(unsigned int vsdr, unsigned int fsdr)
{
	//No shaders given
	if (!vsdr && !fsdr) {
		fprintf(stderr, "create_program called with invalid shader objects, aborting\n");
		return 0;
	}

	//Create program, attach shaders and link program
	unsigned int prog = glCreateProgram();
	if (vsdr) glAttachShader(prog, vsdr);
	if (fsdr) glAttachShader(prog, fsdr);
	glLinkProgram(prog);

	//Check compilation status
	int status;
	glGetProgramiv(prog, GL_LINK_STATUS, &status);

	//Compilation failed
	if (!status) {
		//Get compilation log
		int logsz;
		glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logsz);
		if (logsz > 1) {
			char *buf = new char[logsz +1];
			glGetProgramInfoLog(prog, logsz, 0, buf);
			buf[logsz] = 0;
			printf("linking shader program failed.\nshader program linker log:\n%s\n", 
				buf);
			delete [] buf;
		}

		//Delete program and return nothing
		glDeleteProgram(prog);
		return 0;
	}

	//Return program
	return prog;
}

unsigned int load_shader(int type, const char *fname)
{
	//Open shader's file
	FILE *fp = fopen(fname, "rb");
	if (!fp) {
		fprintf(stderr, "failed to open shader \"%s\": %s\n", fname, strerror(errno));
		return 0;
	}

	//Calculate amount of characters
	fseek(fp, 0, SEEK_END);
	int sz = ftell(fp);
	rewind(fp);

	//Create new char array to hold file's content (terminating with 0)
	char *src = new char[sz + 1];
	fread(src, 1, sz, fp);
	src[sz] = 0;
	fclose(fp);

	//Create shader
	unsigned int sdr = create_shader(type, src);
	delete [] src;

	//Return shader's number
	return sdr;
}

unsigned int load_program(const char *vfname, const char *ffname)
{
	//Vertex shader
	unsigned int vsdr = 0;
	if (vfname)
		if ( !(vsdr = load_shader(GL_VERTEX_SHADER, vfname)) )
			return 0;

	//Fragment shader
	unsigned int fsdr = 0;
	if (ffname)
		if ( !(fsdr = load_shader(GL_FRAGMENT_SHADER, ffname)) )
		{
			glDeleteShader(vsdr);
			return 0;
		}

	//Shaders created - create program
	return create_program(vsdr, fsdr);
}
